package com.xqboss.web.core.config;

import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import com.xqboss.common.enums.BaseEnum;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.EnumerationElementFacetBuilder;
import springfox.documentation.builders.ExampleBuilder;
import springfox.documentation.builders.ModelSpecificationBuilder;
import springfox.documentation.builders.StringElementFacetBuilder;
import springfox.documentation.schema.Annotations;
import springfox.documentation.schema.ModelSpecification;
import springfox.documentation.schema.NumericElementFacetBuilder;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * <p>
 * 适配swagger枚举类型参数显示
 * </p>
 *
 * @author Trent
 * @date 2023/8/18
 */
@Component
//@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class SwaggerEnumBuilderPlugin implements ModelPropertyBuilderPlugin, ExpandedParameterBuilderPlugin , ParameterBuilderPlugin {
    @Override
    public void apply(ModelPropertyContext context) {
        final Class<?> rawPrimaryType = context.getBeanPropertyDefinition().get().getRawPrimaryType();
        //过滤得到目标类型
        if (BaseEnum.class.isAssignableFrom(rawPrimaryType)) {
            //获取CodedEnum的code值
            BaseEnum[] values = (BaseEnum[]) rawPrimaryType.getEnumConstants();
            final List<String> displayValues = Arrays.stream(values).map(e -> e.getValue() + ":" + e.getMsg()).collect(Collectors.toList());
            final List<String> allowList = Arrays.stream(values).map(e -> String.valueOf(e.getValue())).collect(Collectors.toList());
            final AllowableListValues allowableListValues = new AllowableListValues(allowList, rawPrimaryType.getTypeName());
            context.getSpecificationBuilder()
                    .description(context.getSpecificationBuilder().build().getDescription() + ":" + StringUtils.join(displayValues, ","))
                    .facetBuilder(EnumerationElementFacetBuilder.class).allowedValues(allowableListValues).build();
        } else if (LocalDateTime.class.isAssignableFrom(rawPrimaryType)) {
            context.getSpecificationBuilder().type(new ModelSpecificationBuilder().scalarModel(ScalarType.LONG).build());
        }
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }

    @Override
    public void apply(ParameterExpansionContext context) {
        Class<?> rawPrimaryType = context.getFieldType().getErasedType();
        if (BaseEnum.class.isAssignableFrom(rawPrimaryType)) {
            //获取CodedEnum的code值
            BaseEnum[] values = (BaseEnum[]) rawPrimaryType.getEnumConstants();
            final List<String> displayValues = Arrays.stream(values).map(e -> e.getValue() + ":" + e.getMsg()).collect(Collectors.toList());
            final List<String> allowList = Arrays.stream(values).map(e -> String.valueOf(e.getValue())).collect(Collectors.toList());
            final AllowableListValues allowableListValues = new AllowableListValues(allowList, rawPrimaryType.getTypeName());
            context.getRequestParameterBuilder()
                    .description(context.getRequestParameterBuilder().build().getDescription() + ":" + StringUtils.join(displayValues, ","))
                    .query(parameter -> {
                        parameter.enumerationFacet(face -> {
                            face.allowedValues(allowableListValues);
                        });
                    })
                    .build();
        } else if (LocalDateTime.class.isAssignableFrom(rawPrimaryType)) {
            context.getRequestParameterBuilder().query(parameter -> {
                parameter.model(model -> {
                    model.scalarModel(ScalarType.LONG);
                });
            }).example(new ExampleBuilder().value(1645718400000L).build());
        }
    }

    /**
     *
     * @param parameterContext - context that can be used to override the parameter attributes
     */
    @Override
    public void apply(ParameterContext parameterContext) {
        Class<?> rawPrimaryType = parameterContext.resolvedMethodParameter().getParameterType().getErasedType();
        if (BaseEnum.class.isAssignableFrom(rawPrimaryType)) {
            //获取CodedEnum的code值
            BaseEnum[] values = (BaseEnum[]) rawPrimaryType.getEnumConstants();
            final List<String> displayValues = Arrays.stream(values).map(e -> e.getValue() + ":" + e.getMsg()).collect(Collectors.toList());
            final List<String> allowList = Arrays.stream(values).map(e -> String.valueOf(e.getValue())).collect(Collectors.toList());
            final AllowableListValues allowableListValues = new AllowableListValues(allowList, rawPrimaryType.getTypeName());
            parameterContext.requestParameterBuilder()
                    .description(parameterContext.requestParameterBuilder().build().getDescription() + ":" + StringUtils.join(displayValues, ","))
                    .query(parameter -> {
                        parameter.enumerationFacet(face -> {
                            face.allowedValues(allowableListValues);
                        });
                    })
                    .build();
        } else if (LocalDateTime.class.isAssignableFrom(rawPrimaryType)) {
            parameterContext.requestParameterBuilder()
                    .query(parameter -> {
                        parameter.model(model -> {
                            model.scalarModel(ScalarType.LONG);
                        });
                    })
                    .example(new ExampleBuilder().value(1645718400000L).build());
        }
    }
}
